#include <unistd.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <sys/poll.h>
#include <sys/epoll.h>

#include "dataDevice.h"
#include "readConfig.h"
#include "unp.h"
#include "netDevice.h"
#include "config.h"
#include "network.h"

using namespace std;

const char ConfigFile[] = "config";
static const int timeout = 10;

string localWiFiIP;

void turnOnWiFi()
{
	char cmd[100];
	sprintf(cmd, "ifconfig rausb0 %s up", localWiFiIP.c_str());
	system(cmd);
	system("iwconfig rausb0 mode ad-hoc essid wifi-adhoc");
}

void turnOffWiFi()
{
	system("iwconfig rausb0 down");
}

void *networkM(void *null)
{
	string currentMode = "wifi";
	udpClient netMS;
	netMS.Bind("", networkMPort);
	char buff[1000];
	string remoteAddr;
	short unsigned int remotePort;
	int readlen;
	while(true)
	{
		readlen = netMS.Recvfrom((char *)buff, sizeof(buff), remoteAddr, remotePort);
		//cout<<"networkM: remote address "<<remoteAddr<<" port "<<remotePort<<endl;
		if(remoteAddr == "127.0.0.1")
			cout<<"433M broadcast"<<endl;
		else
			cout<<"WiFi broadcast"<<endl;

		if(buff[0] == changeTo433)
		{
			if(currentMode == "wifi")	//turn on 433M device and turn off wifi
				turnOffWiFi();
			
			char on[] = "on";
			netMS.Sendto(on, sizeof(on), "", netMSCommandRPort);
		}
		else if(buff[0] == changeToWiFi)
		{
			if(currentMode == "433")
			{
				turnOnWiFi();
			}

			char off[] = "off";
			netMS.Sendto(off, sizeof(off), "", netMSCommandRPort);
		}
	}
	return (void*)NULL;
}

void *client433(void* null)
{
	//Create 433M device
	FTTMDevice ndd;
	ndd.setup();

	struct sockaddr_in local;
	bzero(&local, sizeof(local));
	local.sin_family = AF_INET;
	local.sin_port = htons(netDeviceListenPort);
	local.sin_addr.s_addr = htonl(INADDR_ANY);

	//Connect to 433M Device
	int dsock = Socket(AF_INET, SOCK_STREAM, 0);
	Connect(dsock, (SA*)&local, sizeof(local));

	//Connect to node Server
	local.sin_port = htons(cmdPort);
	int nsock = Socket(AF_INET, SOCK_STREAM, 0);
	Connect(nsock, (SA*)&local, sizeof(local));

	udpClient netMSCommandR;
	netMSCommandR.Bind("", netMSCommandRPort);

	const int MAX_EVENTS = 3;

	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for dsock
	ev.events = EPOLLIN;
	ev.data.fd = dsock;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, dsock, &ev) == -1)
	{
		perror("epoll_ctl: dsock");
		exit(-1);
	}

	//ADD control for nsock
	ev.events = EPOLLIN;
	ev.data.fd = nsock;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, nsock, &ev) == -1)
	{
		perror("epoll_ctl: nsock");
		exit(-1);
	}

	ev.data.fd = netMSCommandR.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: nsock");
		exit(-1);
	}

	char buff[254];

	int nfds, readn;
	char id = 1;
	udpClient netMSC;

	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if(nfds == -1)
		{
			perror("epoll_wait");
			exit(-1);
		}

		for(int n = 0; n < nfds; ++n)
		{
			//Receive data from 433M device
			if(events[n].data.fd == dsock)
			{
				char len;
				Readn(dsock, &len, 1);
				Readn(dsock, buff, len);
				if(buff[0] == 1)	//Msg from sink node
				{
					if(buff[1] == DFLAG)
						Send(nsock, buff + 2, len - 2, 0);
					else if(buff[1] == CFLAG)	//Send Control Msg to Control Server
						netMSC.Sendto(buff + 2, len - 2, "127.0.0.1", networkMPort);
				}
				cout<<"433M client: Receive "<<len+1<<" bytes(id + data) from 433M device"<<endl;
			}
			//Receive data from node
			else if(events[n].data.fd == nsock)
			{
				readn = Read(nsock, buff + 3, sizeof(buff) - 3);
				buff[0] = readn + 2;
				buff[1] = id;
				buff[2] = DFLAG;
				Send(dsock, buff, readn + 3, 0);	//Send data

				cout<<"433M client: Send "<<readn<<" bytes(id<1 byte> + data) to 433M device"<<endl;
			}
			else if(events[n].data.fd == netMSCommandR.getSocket())
			{
				char buff[100];
				short unsigned int port;	//useless
				string addr;	//useless
				netMSCommandR.Recvfrom((char *)buff, sizeof(buff), addr, port);
				if(addr == "127.0.0.1")
				{
					if(!strcmp(buff, "on"))	//turn on 433M radio and send ACK
					{
						ndd.on();	//turn on 433M radio

						//Send switch success to gateway
						char buff[] = {0x04, id, CFLAG, 0x01, to433Success};
						Send(dsock, buff, sizeof(buff), 0);
					}
					else if(!strcmp(buff, "off"))	//turn off 433M radio
						ndd.off();	//turn off 433M radio
				}
			}
		}
	}

	return (void*)NULL;
}

int main(int argc, char **argv)
{
	int res;
	/* get configure */
	Config configSettings(ConfigFile);
	string deviceName;
	deviceName = configSettings.Read("dataDeviceName", deviceName);
	//cout<<"cmdPort: "<<cmdPort<<endl;
	
	localWiFiIP = configSettings.Read("configSettings.Read", localWiFiIP);

	/* create device */
	dataDevice *device;
	if(deviceName == "ethernet")
	{
		string dataDeviceAddress;
		dataDeviceAddress = configSettings.Read("dataDeviceAddress", dataDeviceAddress);
		int port =  configSettings.Read("dataDevicePort", 0);
		device = new ethernetDataDevice(dataDeviceAddress.c_str(), port);
	}
	else if(deviceName == "serial")
	{
		device = new serialDataDevice("/dev/ttyTX0", "115200");
	}
	else
	{
		device = NULL;
	}

	/* open device */
	device->open();

	/* create socket for receive cmd */
	int fd = Socket(AF_INET, SOCK_STREAM, 0);

	/* build connection address */
	struct sockaddr_in addr;
	bzero(&addr, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(cmdPort);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int reuse = 1;
	Setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
	Bind(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr));

	//accept or send message
	char buff[65536];
	Listen(fd, 5);

	cout<<"Node: Listen on port "<<htons(addr.sin_port)<<endl;

	//FTTMDevice ndd;
	//ndd.setup();

	pthread_t thread433client;
	Pthread_create(&thread433client, NULL, client433, NULL);
		
	pthread_t threadnetworkM;
	Pthread_create(&threadnetworkM, NULL, networkM, NULL);

	while(true)
	{
		struct sockaddr_in cliaddr;
		socklen_t clilen = sizeof(cliaddr);
		int connfd = Accept(fd, (struct sockaddr *)&cliaddr, &clilen);
		char paddr[INET_ADDRSTRLEN];
		cout<<"Node: Receive connect from: "<<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)<<":"<<ntohs(cliaddr.sin_port)<<endl;
		printf("--------------------------connfd:%d\n", connfd);
		struct pollfd fds[2];
		fds[0].fd = connfd;
		fds[0].events = POLLIN;

		fds[1].fd = device->getFd();
		fds[1].events = POLLIN;

		while(true)
		{
			//res = Poll(fds, 2, timeout*1000);
			res = Poll(fds, 2, -1);
			if(res == 0)
			{
				cout<<"Select timeout"<<endl;
				break;
			}
			if(fds[0].revents & POLLIN)	//Read data from gateway
			{
				bzero(buff, sizeof(buff));
				res = recv(connfd, buff, sizeof(buff), 0);
				cout<<"Node: Read "<<res<<endl;
				if(res <= 0)
				{
					cout<<"Disconnected from gateway"<<endl;
					break;
				}
				else if(res > 0)
				{
					cout<<"Node: recv "<<res<<" bytes from gateway"<<endl;
					/*
					for(int i=0;i<res;i++)
						printf("\\x%02x ", buff[i]);
					cout<<endl;
					*/
					/***** write to device*********/
					int wres = device->write(buff, res);
					cout<<"Write to device ok: "<<wres<<endl;
					/***** write to device end *********/

					//Test
					send(connfd, buff, res, 0);

					cout<<"Node sendto gateway."<<endl;
				}
			}

			if(fds[1].revents & POLLIN)	//Read data from serial
			{
				bzero(buff, sizeof(buff));
				/***** read from device*******/
				//cout<<"Read from device......."<<endl;
				int res = device->read(buff, sizeof(buff));
				//cout<<"Node: Read "<<res<<" bytes from device"<<endl;
				if(res == 0)
				{
					cout<<"Disconnected from device"<<endl;
					break;
				}
				else if(res < 0)
				{
					if(errno == EINTR)
						continue;
					cout<<"device error"<<endl;
					exit(1);
				}
				else if(res > 0)
				{
					//cout<<"Read "<<res<<" bytes from device"<<endl;
					/***** read from devicr end *******/

					/**** send data to gateway ******/
					cout<<"Send to gateway: "<<res<<endl;
					for(int i = 0;i<res;i++)
						printf("%02hhx ", buff[i]);
					cout<<endl;
					send(connfd, buff, res, 0);

					/**** send data to gateway end ******/
				}
			}
		}
		Close(connfd);
		cout<<"--------------------------connfd end"<<endl;
	}//end while(true)
	Close(fd); //close listen fd
	return 0;
}//end main

